#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>

/* global variables (available in all functions)*/
pid_t pid_program;//process id of main program
int semafor;



/*this function creates a semaphore set*/
static void create_new_semaphores(int number_of_semaphores){
	key_t key_semaphore;
	key_semaphore = ftok(".", 1000);//ftok: The ftok() function returns a key based on path and id

	if (key_semaphore == -1) {
		printf("[%d]\tCould not obtain semaphore key.\n", (int)pid_program);
		exit(EXIT_FAILURE);

	} else {
		//The semget() function returns the semaphore identifier associated with a key
		//IPC_CREAT: n semaphores are created if key does not already have a semaphore identifier  
		semafor = semget(key_semaphore, number_of_semaphores, IPC_CREAT|0600);
		if(semafor == -1) 
		{
			printf("[%d]\tCould not obtain a semaphore identifier.\n", (int)pid_program);
			exit(EXIT_FAILURE);
		}
		else
		{
			printf("[%d]\tobtained %d as semaphore identifier.\n", (int)pid_program, semafor);
		}
	}
}


/*This function modifies a semaphore according to the index provided*/
static void modify_semaphore(int index_semafora)
{
	int modified_sem;
	
	union semun {
		int val;
		struct semid_ds *buf;
		unsigned short  *array;
		struct seminfo  *__buf;
	};
	
	union semun argumenty;
	argumenty.val = 1;
	/*semctl: if we use 4 arguments, 
		the third argument: SETVAL is the operation we want to perform, doing semval=argumenty.val
		the fourth argument: must be of type union semun.*/
	modified_sem = semctl(semafor, index_semafora, SETVAL, argumenty);
		
	if(modified_sem == -1)
	{
		printf("[%d]\tCould not update semaphore.\n", (int)pid_program);
		exit(EXIT_FAILURE);
	}
	else
	{
		printf("[%d]\tSemaphore %d was updated.\n", (int)pid_program, index_semafora);
	}
}

/* This function clears semaphores*/
static void clear_semaphores()  
{
	int clear_sem;
	/*semctl 
		IPC_RMID: Remove the semaphore-identifier specified by semid from the system 
		and destroy the set of semaphores and semid_ds data structure associated with it.
	*/
	clear_sem = semctl(semafor, 0, IPC_RMID);
	
	if (clear_sem == -1)
	{
		printf("[%d]\tCould not clear semaphore.\n", (int)pid_program);
		exit(EXIT_FAILURE);
	}
	else
	{
		printf("[%d]\tSemaphore %d has been cleared.\n", (int)pid_program, semafor);
	}
}

/* main() function accepts arguments provided by the shell... 
* N.B. the program name is the first arguments: that means we actually expect 3 arguments
*/
int main(int argc, char *argv[]){

printf("hello semaphores !\n");
printf("\n"); // we print an empty line to the standard output (terminal window)

int nbr_subproc;
char* name_pr;//program name that we want to execute
char * path_pr = (char *) malloc(sizeof(char) * 50); //command the will run in the terminal

int liczba_sk;
char * liczba_sk_tekst = (char *) malloc(sizeof(char) * 10);
char * i_tekst = (char *) malloc(sizeof(char) * 10);

int i;
pid_t pid_child;
int status;
	if (argc == 4){
		printf("We have the 3 arguments as expected: \n");
		name_pr = argv[1];	
		nbr_subproc = atoi(argv[2]);
		liczba_sk = atoi(argv[3]);

		if (name_pr != NULL  && nbr_subproc > 0 )
		{
			// main PID.
			pid_program = getpid();
			// Tworzy semafor.
			create_new_semaphores(1);
			// Ustawia semafor.
			modify_semaphore(0);
			// Ustawia zmienną pomocniczą dla fun. execl().
			sprintf(path_pr, "./%s", name_pr);

			// Drukuje informacje.
			printf("[%d]\tWywoływany program: %s.\n", (int)pid_program, name_pr);
			printf("[%d]\tWywoływany program - ścieżka: %s.\n", (int)pid_program, path_pr);

			// Tworzy podaną liczbę procesów.
			for(i = 0; i < nbr_subproc; i++)
			{
				pid_child = fork();
				printf("[%d]\tProces o PIDzie %d został utworzony.\n", (int)pid_program, (int)pid_child);

				sprintf(i_tekst, "0");
				sprintf(liczba_sk_tekst, "%d", liczba_sk);
				
				switch (pid_child)
				{
					case -1:
						printf("[%d]\tBłąd fork()!\n", (int)pid_program);
						exit(EXIT_FAILURE);
						break;
					case 0:
						// Ten kawałek wykona każdy nowy proces potomny.
						if(execl(path_pr, name_pr, i_tekst, liczba_sk_tekst, NULL) == -1)
						{
							printf("[%d]\tBłąd execl()!\n", (int)pid_program);
							exit(EXIT_FAILURE);
						}
						break;
					default:
						// Ten kawałek wykona każdy proces macierzysty.
						break;
				}
			}//for
			
			for(i = 0; i < nbr_subproc; i++)
			{
				pid_child = wait(&status);
				printf("[%d]\tProces - childPID %d has status: %d.\n", (int)pid_program, (int)pid_child, status);
			}
			
			clear_semaphores();
		}//if
		else
		{
			printf("[%d]\tInvalid arguments value!\n", (int)pid_program);
			exit(EXIT_FAILURE);
		}
	} 
	else 
	{
		printf("Sorry: we need some arguments \n");	
		exit(EXIT_FAILURE);
	}

return 0;
}//end of main

